package com.demo.深入理解springmvc源代码;

import java.time.Duration;
import java.util.concurrent.CompletableFuture;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import reactor.core.publisher.Mono;

/**在《深入理解springmvc源代码》-《第4章 spring对web开发的扩展支持》里介绍webflux时有一个例子，模拟webflux下的web请求
 * @author zhangsulei
 *
 */
public class SimulateWebflux {

	private static Logger logger = LoggerFactory.getLogger(SimulateWebflux.class);

	public static void main(String[] args) throws InterruptedException {
		simulateRequestAndReponse();
	}

	private static void simulateRequestAndReponse() throws InterruptedException {
		// 先通过delayRequest获取请求数据，但请求数据2s后才可以被全部接收。数据接收后暂时不会被map和flatMap处理，只有此数据被订阅后，才执行map和flatMap处理，此时
		// 请求数据暂时存在缓存中。map对请求数据进行简单映射，flatMap调用asyncResult获取结果，因为asyncResult返回的也是流数据，所以随后使用flatMap对数据流
		// 进行操作符，可以做到请求时不获取数据，订阅时才开始获取，最后执行订阅，写入响应，此时map和flatMap才开始执行

		// 可以看到先打印了"请求线程执行完成"提示信息，此时主线程会被释放。此后延迟2s后delayRequest会获取到请求数据，之后asyncResult方法会被执行，再延迟
		// 2s后，打印出结果，这个过程便是请求与响应的模拟过程

		// 以下使用webflux技术，其特点是在获取数据和获取响应数据的过程中并不占用主线程，完全异步。同时获取请求数据和响应数据的两个方法也是非阻塞的。在等待一定时间，请求数据
		// 准备好之后，如果此时数据被订阅，则直接执行后续的声明式处理逻辑；否则，数据会被缓存，并等待订阅后处理逻辑才会执行，这也是响应式最大的特点。在等待数据的过程中，是不
		// 占用线程的(下面的模拟方法还占用线程，因为延迟使用的是cpu延迟，如果使用io等多个的延迟，可以做到不占用线程)
		Mono.fromFuture(delayRequest())
			.map(request -> "Hello " + request)
			.flatMap(request -> asyncResult(request))
			.subscribe(response -> logger.info(response));
		logger.info("请求线程执行完成");
		Thread.sleep(3600 * 1000l);
	}

	// 模拟一个获取请求数据的情况，从申请到获取结果需要2s
	private static CompletableFuture<String> delayRequest() {
		return CompletableFuture.supplyAsync(() -> {
			sleep(2000);
			logger.info("获取到request");
			return "Request";
		});
	}

	// 模拟一个长时间执行结果，从执行到获取结果需要2s
	private static Mono<String> asyncResult(String request) {
		logger.info(request);
		return Mono.just("defaultView").delaySubscription(Duration.ofSeconds(2l));
	}

	private static void sleep(long time) {
		try {
			Thread.sleep(time);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}
